package com.csx.governance.traffic.route.swimlane;

import cn.hutool.core.util.StrUtil;
import com.csx.governance.traffic.common.exception.TrafficGovernanceRuleConfigException;
import com.csx.governance.traffic.core.constants.TrafficAttributeConstants;
import com.csx.governance.traffic.core.context.TrafficMessage;
import com.csx.governance.traffic.route.AbstractFilter;
import com.csx.governance.traffic.route.model.swimlane.SwimLaneDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>全链路灰度实例过滤器(泳道实例过滤器)</p>
 * <p>根据请求对象中携带swimLane(泳道ID),读取该泳道中外调应用需要满足的元数据, 然后从服务实例列表中过滤出满足该元数据的实例返回。</p>
 * @author cuisongxu
 * @date 2023/2/25 19:31
 */
public class SwimLaneFilter extends AbstractFilter {

    private static final Logger logger = LoggerFactory.getLogger(SwimLaneFilter.class);

    private final SwimLaneGovernanceRuleLocator swimLaneGovernanceRuleLocator;

    public SwimLaneFilter(SwimLaneGovernanceRuleLocator swimLaneGovernanceRuleLocator) {
        this.swimLaneGovernanceRuleLocator = swimLaneGovernanceRuleLocator;
    }

    @Override
    protected <T> List<T> doFilter(List<T> serviceInstances, Function<T, Map<String, String>> metadataMapping, TrafficMessage trafficMessage) {
        String swimLaneId = trafficMessage.getAttribute(TrafficAttributeConstants.SWIM_LANE, String.class);
        // TODO 这里可能需要区分是由程序定时任务发起的外调导致无泳道ID，还是由于本身没有匹配规则导致无泳道ID
        // 泳道ID不存在，直接返回原始服务实例列表
        if(StrUtil.isBlank(swimLaneId)) {
            return serviceInstances;
        }
        logger.debug("swim lane mode, swimLaneId is {}", swimLaneId);
        String serviceName = trafficMessage.getAttribute(TrafficAttributeConstants.SERVICE_NAME, String.class);
        SwimLaneDefinition.SwimLaneSubDefinition swimLaneSubDefinition = swimLaneGovernanceRuleLocator.getSwimLaneSubDefinition(swimLaneId, serviceName);

        // ************************************************ 实例筛选逻辑 ************************************************
        // 如果当前泳道中没有当前应用的定义,则走当前泳道对应的基线泳道。
        // 如果当前泳道无基线泳道 或者 当前泳道的基线泳道无该应用定义,则返回空实例列表。
        // 如果所选泳道(当前泳道 或 当前泳道的基线泳道)中有当前应用定义,但是没有定义值,说明治理规则配置错误,直接抛出异常。
        // 如果所有泳道(当前泳道 或 当前泳道的基线泳道)中有当前应用定义,且存在定义值,则根据该定义值(labelKey&labelValue)过滤实例,返回过滤后的实例列表。
        if(swimLaneSubDefinition == null) {
            String baseSwimLaneId = swimLaneGovernanceRuleLocator.getBaseSwimLaneIdBySwimLaneId(swimLaneId);
            // 如果当前泳道无基线泳道
            if(baseSwimLaneId == null) {
                logger.warn("current swimLane {} not contains service {} 's definition and current swimLane {} 's baseSwimLaneId is null, so return empty instance list, please update your governance rule config about swimLane definitions", swimLaneId, serviceName, swimLaneId);
                return new ArrayList<>();
            }
            // 当前泳道的基线泳道无该应用定义
            SwimLaneDefinition.SwimLaneSubDefinition baseSwimLaneSubDefinition = swimLaneGovernanceRuleLocator.getSwimLaneSubDefinition(baseSwimLaneId, serviceName);
            if(baseSwimLaneSubDefinition == null) {
                logger.warn("current swimLane {} not contains service {} 's definition and current swimLane {} 's baseSwimLaneId not contains service {} too, so return empty instance list, please update your governance rule config about swimLane definitions", swimLaneId, serviceName, swimLaneId, serviceName);
                return new ArrayList<>();
            }
            swimLaneSubDefinition = baseSwimLaneSubDefinition;
        }

        String labelKey = swimLaneSubDefinition.getSwimLaneKey();
        String labelValue = swimLaneSubDefinition.getSwimLaneValue();

        // 如果泳道中有当前应用定义,但是没有定义值,说明治理规则配置错误,直接抛出异常
        if(labelKey == null || labelValue == null) {
            logger.error("find swimLane {}, but current swimLane 's swimLaneSubDefinition configuration error(swimLaneKey OR swimLaneValue is empty), please check swimLane configuration", swimLaneId);
            throw new TrafficGovernanceRuleConfigException("find swimLane [ "+ swimLaneId + " ],but current swimLane 's swimLaneSubDefinition configuration error(swimLaneKey OR swimLaneValue is empty), please check swimLane configuration");
        }

        // 利用元数据过滤实例
        return serviceInstances.stream()
                .filter(instance -> labelValue.equals(metadataMapping.apply(instance).get(labelKey)))
                .collect(Collectors.toList());
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
